// *********************************************************
//      Copyright (c) Kosta Gaitanis
//      All rights reserved
//
//      k.gaitanis@gmail.com
//
// *********************************************************

#ifndef FACE_H
#define FACE_H

#include <QtOpenGL/QtOpenGL>

#include "Vector.h"

typedef QList<Vector> VectorList;

/**
 * \class Face
 * \brief Represents a face in OpenGL and is capable of drawing itself.
 * \author Kosta Gaitanis
 *
 * Each face has a number of points (usually 3). Each point is defined by
 * 3 vectors :
 *   - The coordinates vector : containing the x,y,z coordinates of the point.
 *   - The normal vector (optional) : containing the normal vector of the face.
 *   - The texture coordinates vector (optional) : containing the coordinates of
 *     the texture pixel to be used when drawn.
 *
 * The order of the points is important and is used to draw the face.
 */
class Face
{
public:
    enum PrimitiveType {
        Line,
        Triangles,
        Quads,
        Polygon,
        TriangleStrip
    };

    /**
     * Constructs a new face with 0 points defined.
     */
    Face(PrimitiveType type);

    /**
     * Destructor
     */
    ~Face();

    /**
     * Copy Constructor
     * @param other The face to be copied.
     */
    Face(const Face &other);

    /**
     * Operator ==
     * Tests if all coordinates, normals and textures are equal between this face
     * and the other face.
     *
     * @param other The face with which this face will be tested.
     * @return True if this face has the same parameters as the other face.
     */
    bool operator ==(const Face& other) const;

    /**
     * Operator !=
     *
     * same as calling !(f1 == f2).
     */
    bool operator !=(const Face& other) const;

    /**
     * Operator =
     * Copies all the points contained in the other face to this face.
     */
    Face& operator =(const Face& other);

    /**
     * Adds a new point to this face at the given position.
     *
     * @param The position where the new point will be added.
     * @param coordinates The coordinates vector.
     * @param normal The normal vector.
     * @param texture The texture coordinates vector.
     */
    void addPoint(int position,
                  const Vector& coordinates,
                  const Vector& normal = Vector(),
                  const Vector& texture = Vector());

    /**
     * Adds a new point to this face at the end of the list of points.
     * This is equal to calling addPoint(count(), coordinates, normal, texture).
     *
     * @param coordinates The coordinates vector.
     * @param normal The normal vector.
     * @param texture The texture coordinates vector.
     */
    void addPoint(const Vector& coordinates,
                  const Vector& normal = Vector(),
                  const Vector& texture = Vector());

    /**
     * Modifies the point of this face at the given position and replaces its
     * coordinate, normal and texture vectors with the new ones.
     *
     * @param position The index of the point to be modified.
     * @param coordinates The new coordinates vector.
     * @param normal The new normal vector.
     * @param texture The new texture coordinates vector.
     */
    void setPoint(int position,
                  const Vector& coordinates,
                  const Vector& normal = Vector(),
                  const Vector& texture = Vector());

    /**
     * Removes the point at the given position.
     *
     * @param position The index of the point to be deleted.
     * @return True if the point was found and deleted.
     */
    void deletePoint(int position);

    /**
     * @return The list of coordinates of all the points in this face.
     */
    const VectorList getCoordinates() const;

    /**
     * @return The list of normal vectors of all the points in this face.
     */
    const VectorList getNormals() const;

    /**
     * @return The list of texture coordinate vectors of all the points in
     *         this face.
     */
    const VectorList getTextures() const;

    /**
     * Returns the coordinates of a point.
     * An empty coordinate is returned if the point was not found.
     *
     * @param position The index of the point.
     * @return The coordinate vector of the point.
     */
    Vector getCoordinate(int position) const;

    /**
     * Sets the coordinates of a point.
     *
     * @param position The index of the point.
     * @param coordinate The new coordinates for the point.
     */
    void setCoordinate(int position, const Vector& coordinate);

    /**
     * Returns the normal vector of a point.
     * An empty coordinate is returned if the point was not found.
     *
     * @param position The index of the point.
     * @return The normal vector of the point.
     */
    Vector getNormal(int position) const;

    /**
     * Sets the normal vector of a point.
     *
     * @param position The index of the point.
     * @param normal The new normal vector for the point.
     */
    void setNormal(int position, const Vector& normal);

    /**
     * Returns the texture coordinates vector of a point.
     * An empty coordinate is returned if the point was not found.
     *
     * @param position The index of the point.
     * @return The texture coordinates vector of the point.
     */
    Vector getTexture(int position) const;

    /**
     * Sets the texture coordinates vector of a point.
     *
     * @param position The index of the point.
     * @param texture The new texture coordinates vector for the point.
     */
    void setTexture(int position, const Vector& texture);

    /**
     * Draws this face. The openGL context must be valid for this to work.
     * This function should not verify the openGL capabilities of the rendering
     * engine.
     */
    void paint();

    /**
     * @return The number of points defined for this face.
     */
    int count() const;

    /**
     * @return True if this face has no points.
     */
    bool isEmpty() const;

    /**
     * @brief sets the color that will be used to draw this face.
     *        This will use glColor() to draw the color.
     *        glColorMaterial must be enabled for this to have any effect.
     *        If glColorMaterial is disabled, Materials should be used.
     * @param color
     */
    void setColor(const QColor& color);

    /**
     * @return The color used to draw this face.
     */
    QColor getColor() const;

protected:
    VectorList m_coordinates;   /// The list of coordinates.
    VectorList m_normals;       /// The list of normals.
    VectorList m_textures;      /// The list of textures.

    QColor m_color;                 /// The color of this face (glColor)
    PrimitiveType m_type;           /// The type of this primitive
};

#endif // FACE_H
